package client

import (
	"hash/crc32"
	"sort"
	"strconv"
)

// Hash 哈希函数
type Hash func(data []byte) uint32

// RegistryTable constains all hashed keys
type RegistryTable struct {
	hash     Hash
	replicas int
	keys     []int // Sorted
	hashMap  map[int]string
}

// New 创建map实例，允许用于替换成自定义的 Hash 函数，也方便测试时替换，默认为 crc32.ChecksumIEEE 算法。
func New(replicas int, fn Hash) *RegistryTable {
	m := &RegistryTable{
		replicas: replicas,
		hash:     fn,
		hashMap:  make(map[int]string),
	}
	if m.hash == nil {
		m.hash = crc32.ChecksumIEEE
	}
	return m
}

// Add 加入节点
func (m *RegistryTable) Add(keys ...string) {
	for _, key := range keys {
		//维护新连接
		_, ok := ClientMap[key]
		if !ok {
			_, c := NewClient("tcp", key)
			ClientMap[key] = c
		}
		for i := 0; i < m.replicas; i++ {
			hash := int(m.hash([]byte(strconv.Itoa(i) + key)))
			m.keys = append(m.keys, hash)
			m.hashMap[hash] = key
		}
	}
	sort.Ints(m.keys)
}

// RenewTable 当注册中心节点发生变化时，更新注册表
func (m *RegistryTable) RenewTable(keys []string) {
	m.hashMap = map[int]string{}
	m.Add(keys...)
}

// Get 获取key所在的节点
func (m *RegistryTable) Get(key string) string {
	if len(m.keys) == 0 {
		return ""
	}

	hash := int(m.hash([]byte(key)))
	// Binary search for appropriate replica.
	idx := sort.Search(len(m.keys), func(i int) bool {
		return m.keys[i] >= hash
	})

	return m.hashMap[m.keys[idx%len(m.keys)]]
}
